-- tb.vhd
-- by Brittle 2009

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_arith.ALL;
Library XilinxCoreLib;
use ieee.std_logic_textio.all;
library std;
use std.textio.all;
use work.admxrc5t2_common.all;
use work.user_defs.all;

ENTITY tb IS
END tb;

ARCHITECTURE sim of tb IS

  component user_app is
    port(
        rst           : in    std_logic;   -- Reset from memory clock domain
        clk           : in    std_logic;   -- Clock from memory clock domain
        reg_in        : in    std_logic_vector(63 downto 0);
        reg_wr        : in    std_logic_vector(255 downto 0); -- 256 bwe
        reg_out       : out   std_logic_vector(2047 downto 0); -- 256 bytes
        mgt_clk       : in    std_logic;
        mgt_rdy       : in    std_logic;
        mgt_txd       : out   mgt_data_t(3 downto 0);
        mgt_txk       : out   std_logic_vector(7 downto 0);
        mgt_rxd       : in    mgt_data_t(3 downto 0);
        mgt_rxk       : in    std_logic_vector(7 downto 0);
        mgt_aligned   : in    std_logic_vector(3 downto 0);
        valid         : in    control_vector_t(max_num_bank - 1 downto 0);
        q             : in    data_vector_t(max_num_bank - 1 downto 0);
        qtag          : in    tag_vector_t(max_num_bank - 1 downto 0);
        ready         : in    control_vector_t(max_num_bank - 1 downto 0);
        req           : out   control_vector_t(max_num_bank - 1 downto 0);
        ce            : out   control_vector_t(max_num_bank - 1 downto 0);
        w             : out   control_vector_t(max_num_bank - 1 downto 0);
        a             : out   address_vector_t(max_num_bank - 1 downto 0);
        tag           : out   tag_vector_t(max_num_bank - 1 downto 0);
        d             : out   data_vector_t(max_num_bank - 1 downto 0);
        be            : out   be_vector_t(max_num_bank - 1 downto 0));
  end component;


  constant sim_size   : integer := 200;
  constant clk_period : time := 10 ns;
  constant rst_period : time := 23 ns;
  constant sim_period : time := 1 us + sim_size * 4 us;

  signal tb_clk  : std_logic;
  signal tb_rst  : std_logic;

  signal reg_in        : std_logic_vector(63 downto 0);
  signal reg_wr        : std_logic_vector(255 downto 0); -- 256 bwe
  signal reg_out       : std_logic_vector(2047 downto 0); -- 256 bytes
  signal mgt_clk       : std_logic;
  signal mgt_rdy       : std_logic;
  signal mgt_txd       : mgt_data_t(3 downto 0);
  signal mgt_txk       : std_logic_vector(7 downto 0);
  signal mgt_rxd       : mgt_data_t(3 downto 0);
  signal mgt_rxk       : std_logic_vector(7 downto 0);
  signal mgt_aligned   : std_logic_vector(3 downto 0);
  signal valid         : control_vector_t(max_num_bank - 1 downto 0);
  signal q             : data_vector_t(max_num_bank - 1 downto 0);
  signal qtag          : tag_vector_t(max_num_bank - 1 downto 0);
  signal ready         : control_vector_t(max_num_bank - 1 downto 0);
  signal req           : control_vector_t(max_num_bank - 1 downto 0);
  signal ce            : control_vector_t(max_num_bank - 1 downto 0);
  signal w             : control_vector_t(max_num_bank - 1 downto 0);
  signal a             : address_vector_t(max_num_bank - 1 downto 0);
  signal tag           : tag_vector_t(max_num_bank - 1 downto 0);
  signal d             : data_vector_t(max_num_bank - 1 downto 0);
  signal be            : be_vector_t(max_num_bank - 1 downto 0);

  signal noise         : std_logic;

BEGIN

  ---------- ---------- test bench setup---------- ----------
  -- generate system clock
  U_clock: process
  begin
    tb_clk <= '0'; wait for clk_period/2;
    tb_clk <= '1'; wait for clk_period/2;
  end process;

  -- generate system reset
  U_reset: process
  begin
    tb_rst <= '1'; wait for rst_period; tb_rst <= '0'; wait;
  end process;

  -- hard timeout for simulation
  U_timeout: process
  begin
    wait for sim_period;
    assert false report "NONE. Simulation timeout!" severity failure;
  end process;

  ---------- ---------- device under test ---------- ----------
  DUT: user_app
    port map (
      rst            => tb_rst        ,
      clk            => tb_clk        ,

      reg_in         => reg_in        ,
      reg_wr         => reg_wr        ,
      reg_out        => reg_out       ,

      mgt_clk        => mgt_clk       ,
      mgt_rdy        => mgt_rdy       ,
      mgt_txd        => mgt_txd       ,
      mgt_txk        => mgt_txk       ,
      mgt_rxd        => mgt_rxd       ,
      mgt_rxk        => mgt_rxk       ,
      mgt_aligned    => mgt_aligned   ,

      valid          => valid         ,
      q              => q             ,
      qtag           => qtag          ,
      ready          => ready         ,
      req            => req           ,
      ce             => ce            ,
      w              => open          ,
      a              => a             ,
      tag            => open          ,
      d              => open          ,
      be             => open          );

  ---------- ---------- MGT interface ---------- ----------
  mgt_clk <= tb_clk;
  mgt_rdy <= '1';
  mgt_rxd(0) <= (others => '0');
  mgt_rxd(1) <= (others => '0');
  mgt_rxd(2) <= (others => '0');
  mgt_rxd(3) <= (others => '0');
  mgt_rxk <= (others => '0');
  mgt_aligned <= (others => '1');

  ---------- ---------- external memory interface ---------- ----------
  qtag(0) <= (others => '0');
  -- memory grant 1 clock cycle after request
  U_grant: process
  begin
    noise <= '0';
    wait for clk_period * 100;
    noise <= '0';
    wait for clk_period * 2;
  end process;
  U_arbiter: process
  begin
    wait until rising_edge(tb_clk);

    if tb_rst = '1' then
      ready(0) <= '0';
    else
      ready(0) <= req(0) and not noise;
    end if;
  end process;
  -- memory data from file
  U_data: process
    file data_in : TEXT open read_mode is "../dat/nbody_81920.sim";
    variable s : line;
    variable v : std_logic_vector(31 downto 0);
    variable lno : integer := 0;
  begin

    wait until rising_edge(tb_clk);

    if ce(0) = '1' then
      if lno > conv_integer(unsigned(a(0))) then
        file_close(data_in);
        file_open(data_in, "../dat/nbody_81920.sim", read_mode);
        lno := 0;
      end if;
      if lno < conv_integer(unsigned(a(0))) then
        while lno < conv_integer(unsigned(a(0))) loop
          readline(data_in, s); lno := lno + 1;
        end loop;
      end if;
      readline(data_in, s); lno := lno + 1;
      hread(s, v); q(0)(31  downto 0)  <= v;
      hread(s, v); q(0)(63  downto 32) <= v;
      hread(s, v); q(0)(95  downto 64) <= v;
      hread(s, v); q(0)(127 downto 96) <= v;
      valid(0) <= '1';
    else
      q(0) <= (others => 'U');
      valid(0) <= '0';
    end if;
  end process;

  ---------- ---------- FPGA/host interface ---------- ----------
  U_control: process
    variable p_cnt : integer := 0;
    variable i : integer := 0;
    variable l : line;
  begin

    reg_in <= (others => 'U');
    reg_wr <= (others => '0');

    -- un-align clock with signals
    wait for 3 ns;

    -- wait after reset
    wait for clk_period * 10;

    -- write N
    reg_in <= X"00000000000000C7"; -- total 200 particles
    reg_wr <= (255 downto 20 => '0') & "1111" & (15 downto 0 => '0');
    wait for clk_period;

    -- write initial i value
    reg_in <= X"0000000000000000"; -- start from the first particle
    reg_wr <= (255 downto 24 => '0') & "1111" & (19 downto 0 => '0');
    wait for clk_period;

    -- write end i value (process 'sim_size' particles)
    reg_in <= conv_std_logic_vector(sim_size-1, 64); 
    reg_wr <= (255 downto 28 => '0') & "1111" & (23 downto 0 => '0');
    wait for clk_period;

    -- write EPS
    reg_in <= X"0000000000000000"; -- EPS = 0
    reg_wr <= (255 downto 32 => '0') & "1111" & (27 downto 0 => '0');
    wait for clk_period;
    reg_wr <= (others => '0');

    -- wait for start
    wait for clk_period * 10;

    -- kick start
    reg_in <= X"0000000000000001";
    reg_wr <= (255 downto 4 => '0') & "1111";
    wait for clk_period;
    reg_wr <= (others => '0');

    -- wait for result
    wait for 140 us;

    -- loop to read any data in the FIFO
    reg_in <= X"0000000000000002";
    L1: loop

      wait until rising_edge(tb_clk);

      if p_cnt < conv_integer(unsigned(reg_out(31 downto 0))) then
        write(l, "[");
        hwrite(l, reg_out(63 downto 32));
        write(l, " ");
        hwrite(l, reg_out(95 downto 64));
        write(l, " ");
        hwrite(l, reg_out(127 downto 96));
        write(l, "]");
        writeline(OUTPUT, l);

        reg_wr <= (255 downto 1 => '0') & "1";
        wait until rising_edge(tb_clk);
        reg_wr <= (others => '0');
        wait until rising_edge(tb_clk);

        p_cnt := p_cnt + 1;
      end if;

      if p_cnt = sim_size then
        assert false report "NONE. Simulation Completed." severity failure;
      end if;

    end loop;

  end process;

END sim;
